package com.kgis.map.handler.feature {
    import com.kgis.map.Map;
    import com.kgis.map.common.enum.MapActionEnum;
    import com.kgis.map.events.FeatureEvent;
    import com.kgis.map.feature.Feature;
    import com.kgis.map.geometry.basetype.Pixel;
    import com.kgis.map.handler.mouse.ClickHandler;
    import com.kgis.map.layer.FeatureLayer;
    import flash.display.Sprite;
    import flash.events.MouseEvent;
    import flash.geom.Rectangle;

    /**
     * 单击选择、 拉框选择
     * 按住CTRL键 增加到选择集.
     * 按住SHIFT键 从选择集移除本次选择的设施.
     */
    public class SelectFeaturesHandler extends ClickHandler {

        private var _unselectedLayer:Vector.<FeatureLayer> = new Vector.<FeatureLayer>();

        /**
         * Callback function onSelectionUpdated(features:Array):void
         */
        private var _onSelectionUpdated:Function = null;

        /**
         * Callback function onSelectedFeature(feature:Feature):void
         */
        private var _onSelectedFeature:Function = null;

        /**
         * Callback function onUnselectedFeature(feature:Feature):void
         */
        private var _onUnselectedFeature:Function = null;

        /**
         * Sprite used to display the selection box.
         */
        private var _drawContainer:Sprite = new Sprite();

        /**
         * Style of the selection area: border thin (default=1)
         */
        private var _selectionAreaBorderThin:Number = 1;

        /**
         * Style of the selection area: border color (default=0xFFCC00)
         */
        private var _selectionAreaBorderColor:uint = 0xFFCC00;

        /**
         * Style of the selection area: fill color (default=0xCC0000)
         */
        private var _selectionAreaFillColor:uint = 0xCC0000;

        /**
         * Style of the selection area: opacity (default=0.2)
         */
        private var _selectionAreaFillOpacity:Number = 0.2;

        private var _enableClickSelection:Boolean;
        private var _enableBoxSelection:Boolean;
        private var _enableSelection:Boolean;

        /**
         * Constructor of the handler.
         * @param map the map associated to the handler
         * @param active boolean defining if the handler is active or not
         */
        public function SelectFeaturesHandler(map:Map, handlername:String, enableClickSelection:Boolean = false, enableBoxSelection:Boolean = false) {
            super(map, handlername);
            if (this.map) {
                this.map.addChild(_drawContainer);
            }

            this.enableClickSelection = enableClickSelection;
            this.enableBoxSelection = enableBoxSelection;

        }

        public function set enableClickSelection(value:Boolean):void {
            if (value)
                this.click = this.selectByClick;
            else
                this.click = null;
        }

        public function set enableBoxSelection(value:Boolean):void {
            if (value) {
                this.drag = this.drawSelectionBox;
                this.drop = this.selectByBox;
            }
            else {
                this.drag = null;
                this.drop = null;
            }
        }

        /**
         * On selection updated function getter and setter
         */
        public function get onSelectionUpdated():Function {
            return this._onSelectionUpdated;
        }

        public function set onSelectionUpdated(value:Function):void {
            this._onSelectionUpdated = value;
        }

        /**
         * On selected feature function getter and setter
         */
        public function get onSelectedFeature():Function {
            return this._onSelectedFeature;
        }

        public function set onSelectedFeature(value:Function):void {
            this._onSelectedFeature = value;
        }

        /**
         * On unselected feature function getter and setter
         */
        public function get onUnselectedFeature():Function {
            return this._onUnselectedFeature;
        }

        public function set onUnselectedFeature(value:Function):void {
            this._onUnselectedFeature = value;
        }

        /**
         * Selection geometry's border thin getter and setter
         */
        public function get selectionAreaBorderThin():Number {
            return this._selectionAreaBorderThin;
        }

        public function set selectionAreaBorderThin(value:Number):void {
            this._selectionAreaBorderThin = value;
        }

        /**
         * Selection geometry's border color getter and setter
         */
        public function get selectionAreaBorderColor():uint {
            return this._selectionAreaBorderColor;
        }

        public function set selectionAreaBorderColor(value:uint):void {
            this._selectionAreaBorderColor = value;
        }

        /**
         * Selection geometry's fill color getter and setter
         */
        public function get selectionAreaFillColor():uint {
            return this._selectionAreaFillColor;
        }

        public function set selectionAreaFillColor(value:uint):void {
            this._selectionAreaFillColor = value;
        }

        /**
         * Selection geometry's fill opacity thin getter and setter
         */
        public function get selectionAreaFillOpacity():Number {
            return this._selectionAreaFillOpacity;
        }

        public function set selectionAreaFillOpacity(value:Number):void {
            this._selectionAreaFillOpacity = value;
        }

        /**
         * Array of the selected features
         */
        public function get selectedFeatures():Vector.<Feature> {
            return this.map.selectedFeatures;
        }

        /**
         * Set the map associated to the handler.
         * The current selection is cleared and the array of the layers to treat
         * is reset to a void array.
         */
        override public function set map(value:Map):void {
            if (this.map) {
                this.map.removeChild(_drawContainer);
            }
            super.map = value;
            if (this.map) {
                this.map.addChild(_drawContainer);
            }
        }

        /**
         * Add the listeners to the associated map
         */
        override protected function registerListeners():void {
            // Listeners of the super class
            super.registerListeners();
            // Listeners of the associated map
            if (this.map) {
                this.map.addEventListener(FeatureEvent.FEATURE_SELECTED, this.onSelected);
                this.map.addEventListener(FeatureEvent.FEATURE_UNSELECTED, this.onUnselected);
            }
        }

        /**
         * Remove the listeners to the associated map
         */
        override protected function unregisterListeners():void {
            // Listeners of the associated map
            if (this.map) {
                this.map.removeEventListener(FeatureEvent.FEATURE_SELECTED, this.onSelected);
                this.map.removeEventListener(FeatureEvent.FEATURE_UNSELECTED, this.onUnselected);
            }
            // Listeners of the super class
            super.unregisterListeners();
        }

        /**
         * If defined, use the onSelectedFeature callback function for all the
         * features of the input event.
         * If the array of layers is defined, only the features of these layers
         * are treated.
         * @param evt the FeatureEvent
         */
        private function onSelected(evt:FeatureEvent):void {
            this.onSomething(evt, this.onSelectedFeature);
        }

        /**
         * If defined, use the onUnselectedFeature callback function for all the
         * features of the input event.
         * If the array of layers is defined, only the features of these layers
         * are treated.
         * @param evt the FeatureEvent
         */
        private function onUnselected(evt:FeatureEvent):void {
            this.onSomething(evt, this.onUnselectedFeature);
        }

        /**
         * Generic function called by all the onSelected and
         * onUnselected functions.
         * If the array of layers is defined, only the features of these layers
         * are treated.
         * @param evt the FeatureEvent that defines the array of the features to
         * treat
         * @param updateStyleFeature the function to use for updating the style
         * of the features
         * @param onSomethingFeature the callback function to use for each of
         * the features
         */
        private function onSomething(evt:FeatureEvent, onSomethingFeature:Function):void {
            var i:int, layer:FeatureLayer, layersTmp:Array = new Array();

            if (this._dragging)
                return;

            for each (var feature:Feature in evt.features) {
                if (onSomethingFeature != null) {
                    onSomethingFeature(feature);
                }

                if (evt.type == FeatureEvent.FEATURE_SELECTED) {
                    feature.selected = true;
                }
                else if (evt.type == FeatureEvent.FEATURE_UNSELECTED) {
                    feature.selected = false;
                }

                trace("当前选择集: => " + this.selectedFeatures.length + " features selected");

                feature.executeDrawing();
            }

        }

        /**
         * (Un)select all the features that contain the location clicked (the
         * selectionBuffer is used to enlarge the selection area).
         * If the array of layers is defined, only the features of these layers
         * are treated.
         * @param evt the MouseEvent (useful for the position of MouseUp and for
         * the status of the CTRL and SHIFT keys)
         */
        private function selectByClick(p:Pixel, features:Vector.<Feature>, evt:MouseEvent):void {
            // A point and a selectionBuffer define a selection box...
            /*         //点击feature后查看这个feature在不在这个空间分析出的设施列表里，如果不在则加入
               if(this._mouseEvent.target is Feature)
               {
               if(features.indexOf(this._mouseEvent.target)<0){
               features.push(this._mouseEvent.target);
               }
             }*/

            selectByBox(p, features, evt);
        }

        private function selectByBox(p:Pixel, features:Vector.<Feature>, evt:MouseEvent):void {
            // Clear the selection drawing
            _drawContainer.graphics.clear();
            var featuresToSelect:Vector.<Feature> = new Vector.<Feature>();
            for each (var feature:Feature in features) {
                if (this._unselectedLayer.indexOf(feature.layer) < 0) {
                    featuresToSelect.push(feature);
                }
            }

            // Update the selection
            if (this._shiftKey && (!this._ctrlKey)) {
                this.unselect(featuresToSelect);
            }
            else {
                this.select(featuresToSelect, this._ctrlKey);
            }

            this.map.operateMode = MapActionEnum.MAP_PAN;
            //通知外部改变鼠标形状
            this.map.dispatchEvent(new FeatureEvent(FeatureEvent.FEATURE_SELECTBYBOX, null));

        }

        /**
         * Add or replace features to the current selection.
         * The features are asserted to be in one of the layers to manage.
         * @param featuresToSelect the array of the features to add / replace by
         * @param additiveMode if true the input features are added to the
         * current selection ; if false they replace it
         */
        private function select(featuresToSelect:Vector.<Feature>, additiveMode:Boolean = false):void {
            var selectionUpdated:Boolean = false;
            var removedFeatures:Vector.<Feature> = new Vector.<Feature>(); // the features to remove of the current selection
            var feature:Feature;
            var fevt:FeatureEvent;
            // If the current selection is not void, first we restrict the
            // features of the input array to the really new selected features
            // and we unselect the features that need it.

            if (this.selectedFeatures.length > 0) {
                var sf:Vector.<Feature> = new Vector.<Feature>(); // the features to keep selected
                var i:int, found:Boolean;
                if (!additiveMode) {
                    // If additive mode is not selected, remove all the current
                    // selected features that are not in the input array.
                    for each (feature in this.selectedFeatures) {
                        for (i = 0, found = false; (!found) && (i < featuresToSelect.length); i++) {
                            if (feature == featuresToSelect[i]) {
                                found = true;
                            }
                        }
                        if (found) {
                            // If this currently selected feature is reselected,
                            // keep it in the current selection
                            sf.push(feature);
                        }
                        else {
                            // Otherwise add it to the features to remove
                            //feature.selected=false;
                            removedFeatures.push(feature);
                        }
                    }
                }
                // Remove from the input array all the reselected features
                var nsf:Vector.<Feature> = new Vector.<Feature>(); // the really new features in the input array
                for each (feature in featuresToSelect) {
                    for (i = 0, found = false; (!found) && (i < this.selectedFeatures.length); i++) {
                        if (feature == this.selectedFeatures[i]) {
                            found = true;
                        }
                    }
                    if (!found) {
                        nsf.push(feature);
                    }
                }
                featuresToSelect = nsf;
            }

            // Dispatch a FEATURE_UNSELECTED event for all the unselected features
            if (this.map && (removedFeatures.length > 0)) {

                fevt = new FeatureEvent(FeatureEvent.FEATURE_UNSELECTED, null, additiveMode);
                fevt.features = removedFeatures;
                this.map.dispatchEvent(fevt);
                trace("移除个数:" + removedFeatures.length);
            }
            // Dispatch a FEATURE_SELECTED event for all the newly selected features
            if (this.map && (featuresToSelect.length > 0)) {

                fevt = new FeatureEvent(FeatureEvent.FEATURE_SELECTED, null, additiveMode);
                fevt.features = featuresToSelect;
                this.map.dispatchEvent(fevt);
                trace("选择个数:" + featuresToSelect.length);
            }
            // Log the selection modification
            //trace("SelectFeaturesHandler: " + featuresToSelect.length + " new features selected with additive mode " + ((additiveMode) ? "ON" : "OFF") + " rejected features => " + this.selectedFeatures.length + " features selected");
            // if the selection has been updated, use the associated callback
            if (selectionUpdated && (this.onSelectionUpdated != null)) {
                this.onSelectionUpdated(this.selectedFeatures);
            }
        }

        /**
         * Remove features from the current selection.
         * The features are asserted to be in one of the layers to manage.
         * @param featuresToUnselect the array of the features to remove
         */
        private function unselect(featuresToUnselect:Vector.<Feature>):void {
            // Unselect the input features that are registred as selected
            var selectionUpdated:Boolean = false;
            var removedFeatures:Vector.<Feature> = new Vector.<Feature>(); // the features really removed from the current selection
            var feature:Feature;
            var i:int, found:Boolean;
            for each (feature in featuresToUnselect) {
                for (i = 0, found = false; (!found) && (i < this.selectedFeatures.length); i++) {
                    if (feature == this.selectedFeatures[i]) {
                        found = true;
                        removedFeatures.push(feature);
                        selectionUpdated = true;
                    }
                }
                if (!found) {
                    trace("unselect warning: unselected feature, nothing to do");
                }
            }
            // Dispatch a FEATURE_UNSELECTED event for all the unselected features
            if (this.map && (removedFeatures.length > 0)) {

                var fevt:FeatureEvent = new FeatureEvent(FeatureEvent.FEATURE_UNSELECTED, null);
                fevt.features = removedFeatures;
                this.map.dispatchEvent(fevt);
            }
            // Log the selection modification
            trace("SelectFeaturesHandler: " + removedFeatures.length + " features removed from the selection => " + this.selectedFeatures.length + " features selected");
            // if the selection has been updated, use the associated callback
            if (selectionUpdated && (this.onSelectionUpdated != null)) {
                this.onSelectionUpdated(this.selectedFeatures);
            }
        }

        /**
         * Clear the current selection.
         */
        public function clearSelection():void {
            var selectionUpdated:Boolean = (this.selectedFeatures.length > 0);
            // If the selection is void there is nothing to do
            if (!selectionUpdated) {
                return;
            }
            // Clear the selection
            var removedFeatures:Vector.<Feature> = this.selectedFeatures;

            // Dispatch a FEATURE_UNSELECTED event for all the unselected features
            if (this.map && (removedFeatures.length > 0)) {
                var fevt:FeatureEvent = new FeatureEvent(FeatureEvent.FEATURE_UNSELECTED, null);
                fevt.features = removedFeatures;
                this.map.dispatchEvent(fevt);
            }
            // Log the selection modification
            trace("SelectFeaturesHandler: selection cleared of its " + removedFeatures.length + " features");
            // if the selection has been updated, use the associated callback
            if (selectionUpdated && (this.onSelectionUpdated != null)) {
                this.onSelectionUpdated(this.selectedFeatures);
            }
        }

        /**
         * Display the selection box that the user is drawing.
         * @param evt the MouseEvent
         */
        private function drawSelectionBox(evt:MouseEvent):void {
            // Compute the selection box (in pixels)
            var rect:Rectangle = this.selectionBoxPixels(new Pixel(evt.currentTarget.mouseX, evt.currentTarget.mouseY));
            // Display the selection box
            _drawContainer.graphics.clear();
            _drawContainer.graphics.lineStyle(this.selectionAreaBorderThin, this.selectionAreaBorderColor);
            _drawContainer.graphics.beginFill(this.selectionAreaFillColor, this.selectionAreaFillOpacity);
            _drawContainer.graphics.drawRect(rect.x, rect.y, rect.width, rect.height);
            _drawContainer.graphics.endFill();
        }

        public function get unselectedLayer():Vector.<FeatureLayer> {
            return _unselectedLayer;
        }

        public function set unselectedLayer(value:Vector.<FeatureLayer>):void {
            _unselectedLayer = value;
        }


    }
}